package com.powerbank.device.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.powerbank.device.dto.DeviceCommandDTO;
import com.powerbank.device.dto.DeviceStatusDTO;
import com.powerbank.device.dto.SlotStatusDTO;
import com.powerbank.device.service.EnhancedDeviceCommunicationService;
import com.powerbank.device.service.MqttMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * 增强版设备通信服务实现类
 */
@Slf4j
@Service
public class EnhancedDeviceCommunicationServiceImpl implements EnhancedDeviceCommunicationService {

    @Autowired
    private MqttMessageService mqttMessageService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // Redis Key前缀
    private static final String COMMAND_RESULT_PREFIX = "device:command:result:";
    private static final String DEVICE_STATUS_PREFIX = "device:status:";
    private static final String SLOT_STATUS_PREFIX = "device:slot:status:";

    @Override
    public String sendUnlockCommand(String deviceCode, Integer slotNo) {
        String commandId = IdUtil.simpleUUID();
        
        DeviceCommandDTO command = new DeviceCommandDTO();
        command.setCommandId(commandId);
        command.setCommandType("UNLOCK");
        command.setDeviceCode(deviceCode);
        command.setSlotNo(slotNo);
        command.setTimeout(30);
        command.setPriority(1);
        command.setSendTime(LocalDateTime.now());

        boolean sent = mqttMessageService.sendDeviceCommand(deviceCode, command);
        
        if (sent) {
            // 将指令结果初始化为等待状态
            String resultKey = COMMAND_RESULT_PREFIX + commandId;
            redisTemplate.opsForValue().set(resultKey, "PENDING", 60, TimeUnit.SECONDS);
            
            log.info("开锁指令发送成功 - 设备: {}, 插槽: {}, 指令ID: {}", deviceCode, slotNo, commandId);
            return commandId;
        } else {
            log.error("开锁指令发送失败 - 设备: {}, 插槽: {}", deviceCode, slotNo);
            return null;
        }
    }

    @Override
    public String sendLockCommand(String deviceCode, Integer slotNo) {
        String commandId = IdUtil.simpleUUID();
        
        DeviceCommandDTO command = new DeviceCommandDTO();
        command.setCommandId(commandId);
        command.setCommandType("LOCK");
        command.setDeviceCode(deviceCode);
        command.setSlotNo(slotNo);
        command.setTimeout(30);
        command.setPriority(1);
        command.setSendTime(LocalDateTime.now());

        boolean sent = mqttMessageService.sendDeviceCommand(deviceCode, command);
        
        if (sent) {
            String resultKey = COMMAND_RESULT_PREFIX + commandId;
            redisTemplate.opsForValue().set(resultKey, "PENDING", 60, TimeUnit.SECONDS);
            
            log.info("锁定指令发送成功 - 设备: {}, 插槽: {}, 指令ID: {}", deviceCode, slotNo, commandId);
            return commandId;
        } else {
            log.error("锁定指令发送失败 - 设备: {}, 插槽: {}", deviceCode, slotNo);
            return null;
        }
    }

    @Override
    public String queryDeviceStatus(String deviceCode) {
        String commandId = IdUtil.simpleUUID();
        
        DeviceCommandDTO command = new DeviceCommandDTO();
        command.setCommandId(commandId);
        command.setCommandType("QUERY_STATUS");
        command.setDeviceCode(deviceCode);
        command.setTimeout(10);
        command.setPriority(2);
        command.setSendTime(LocalDateTime.now());

        boolean sent = mqttMessageService.sendDeviceCommand(deviceCode, command);
        
        if (sent) {
            String resultKey = COMMAND_RESULT_PREFIX + commandId;
            redisTemplate.opsForValue().set(resultKey, "PENDING", 30, TimeUnit.SECONDS);
            
            log.info("状态查询指令发送成功 - 设备: {}, 指令ID: {}", deviceCode, commandId);
            return commandId;
        } else {
            log.error("状态查询指令发送失败 - 设备: {}", deviceCode);
            return null;
        }
    }

    @Override
    public String querySlotStatus(String deviceCode, Integer slotNo) {
        String commandId = IdUtil.simpleUUID();
        
        DeviceCommandDTO command = new DeviceCommandDTO();
        command.setCommandId(commandId);
        command.setCommandType("QUERY_SLOT");
        command.setDeviceCode(deviceCode);
        command.setSlotNo(slotNo);
        command.setTimeout(10);
        command.setPriority(2);
        command.setSendTime(LocalDateTime.now());

        boolean sent = mqttMessageService.sendDeviceCommand(deviceCode, command);
        
        if (sent) {
            String resultKey = COMMAND_RESULT_PREFIX + commandId;
            redisTemplate.opsForValue().set(resultKey, "PENDING", 30, TimeUnit.SECONDS);
            
            log.info("插槽状态查询指令发送成功 - 设备: {}, 插槽: {}, 指令ID: {}", deviceCode, slotNo, commandId);
            return commandId;
        } else {
            log.error("插槽状态查询指令发送失败 - 设备: {}, 插槽: {}", deviceCode, slotNo);
            return null;
        }
    }

    @Override
    public int batchQueryDeviceStatus(String[] deviceCodes) {
        int successCount = 0;
        
        for (String deviceCode : deviceCodes) {
            try {
                String commandId = queryDeviceStatus(deviceCode);
                if (commandId != null) {
                    successCount++;
                }
                
                // 避免并发过高，添加小延时
                Thread.sleep(10);
                
            } catch (Exception e) {
                log.error("批量查询设备状态失败 - 设备: {}, 错误: {}", deviceCode, e.getMessage());
            }
        }
        
        log.info("批量查询设备状态完成 - 总数: {}, 成功: {}", deviceCodes.length, successCount);
        return successCount;
    }

    @Override
    public String getCommandResult(String commandId) {
        try {
            String resultKey = COMMAND_RESULT_PREFIX + commandId;
            Object result = redisTemplate.opsForValue().get(resultKey);
            return result != null ? result.toString() : null;
            
        } catch (Exception e) {
            log.error("获取指令执行结果失败 - 指令ID: {}, 错误: {}", commandId, e.getMessage());
            return null;
        }
    }

    @Override
    public boolean waitForCommandCompletion(String commandId, int timeout) {
        try {
            long startTime = System.currentTimeMillis();
            long timeoutMillis = timeout * 1000L;
            
            while (System.currentTimeMillis() - startTime < timeoutMillis) {
                String result = getCommandResult(commandId);
                
                if (result != null && !"PENDING".equals(result)) {
                    log.info("指令执行完成 - 指令ID: {}, 结果: {}", commandId, result);
                    return "SUCCESS".equals(result);
                }
                
                // 每100ms检查一次
                Thread.sleep(100);
            }
            
            log.warn("指令执行超时 - 指令ID: {}, 超时时间: {}秒", commandId, timeout);
            return false;
            
        } catch (Exception e) {
            log.error("等待指令执行完成异常 - 指令ID: {}, 错误: {}", commandId, e.getMessage());
            return false;
        }
    }

    @Override
    public boolean cancelCommand(String commandId) {
        try {
            String resultKey = COMMAND_RESULT_PREFIX + commandId;
            redisTemplate.opsForValue().set(resultKey, "CANCELLED", 10, TimeUnit.SECONDS);
            
            log.info("指令已取消 - 指令ID: {}", commandId);
            return true;
            
        } catch (Exception e) {
            log.error("取消指令失败 - 指令ID: {}, 错误: {}", commandId, e.getMessage());
            return false;
        }
    }

    @Override
    public DeviceStatusDTO getCachedDeviceStatus(String deviceCode) {
        try {
            String statusKey = DEVICE_STATUS_PREFIX + deviceCode;
            Object statusObj = redisTemplate.opsForValue().get(statusKey);
            
            if (statusObj != null) {
                return JSONUtil.toBean(statusObj.toString(), DeviceStatusDTO.class);
            }
            
            return null;
            
        } catch (Exception e) {
            log.error("获取缓存设备状态失败 - 设备: {}, 错误: {}", deviceCode, e.getMessage());
            return null;
        }
    }

    @Override
    public SlotStatusDTO getCachedSlotStatus(String deviceCode, Integer slotNo) {
        try {
            String statusKey = SLOT_STATUS_PREFIX + deviceCode + ":" + slotNo;
            Object statusObj = redisTemplate.opsForValue().get(statusKey);
            
            if (statusObj != null) {
                return JSONUtil.toBean(statusObj.toString(), SlotStatusDTO.class);
            }
            
            return null;
            
        } catch (Exception e) {
            log.error("获取缓存插槽状态失败 - 设备: {}, 插槽: {}, 错误: {}", deviceCode, slotNo, e.getMessage());
            return null;
        }
    }

    /**
     * 更新设备状态缓存
     */
    public void updateDeviceStatusCache(String deviceCode, DeviceStatusDTO status) {
        try {
            String statusKey = DEVICE_STATUS_PREFIX + deviceCode;
            String statusJson = JSONUtil.toJsonStr(status);
            redisTemplate.opsForValue().set(statusKey, statusJson, 5, TimeUnit.MINUTES);
            
            log.debug("更新设备状态缓存 - 设备: {}", deviceCode);
            
        } catch (Exception e) {
            log.error("更新设备状态缓存失败 - 设备: {}, 错误: {}", deviceCode, e.getMessage());
        }
    }

    /**
     * 更新插槽状态缓存
     */
    public void updateSlotStatusCache(String deviceCode, SlotStatusDTO slotStatus) {
        try {
            String statusKey = SLOT_STATUS_PREFIX + deviceCode + ":" + slotStatus.getSlotNo();
            String statusJson = JSONUtil.toJsonStr(slotStatus);
            redisTemplate.opsForValue().set(statusKey, statusJson, 5, TimeUnit.MINUTES);
            
            log.debug("更新插槽状态缓存 - 设备: {}, 插槽: {}", deviceCode, slotStatus.getSlotNo());
            
        } catch (Exception e) {
            log.error("更新插槽状态缓存失败 - 设备: {}, 插槽: {}, 错误: {}", 
                    deviceCode, slotStatus.getSlotNo(), e.getMessage());
        }
    }

    /**
     * 更新指令执行结果
     */
    public void updateCommandResult(String commandId, String result) {
        try {
            String resultKey = COMMAND_RESULT_PREFIX + commandId;
            redisTemplate.opsForValue().set(resultKey, result, 60, TimeUnit.SECONDS);
            
            log.debug("更新指令执行结果 - 指令ID: {}, 结果: {}", commandId, result);
            
        } catch (Exception e) {
            log.error("更新指令执行结果失败 - 指令ID: {}, 错误: {}", commandId, e.getMessage());
        }
    }
}